#include <map>
#include <string>
#include <iterator>
#include <iostream>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "helper.hpp"
#include "interface.hpp"
#include "udp_server.hpp"

//#define DEBUG
#define DEBUG logger

pthread_t udp_client_thread;

static int create_udp_socket()
{
    int reuse = 1;
    int serv_sock; 
    int port = 2323;
    struct sockaddr_in serv_addr;

    if ((serv_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1){
      perror("socket");
      exit(-1);
    }

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(port);

    if(setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1){
        perror("SO_REUSEADDR");
        exit(1);
    }
 
    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
     
    if (bind(serv_sock, (struct sockaddr* ) &serv_addr, sizeof(serv_addr)) == -1){
        perror("bind");
        exit(1);
    }
    
    return serv_sock;
}

static void* udp_client_handler(void* obj)
{   
    int fd;
    char read_buffer[4096];
    struct sockaddr_in cli_addr;
    socklen_t slen = sizeof(cli_addr);

    fd = create_udp_socket();

    for(;;) {
        bzero(read_buffer,sizeof(read_buffer));

        if (recvfrom(fd, read_buffer, sizeof(read_buffer), 0, 
                    (struct sockaddr*)&cli_addr, &slen) == -1){
            perror("recvfrom");
            continue;
        }

        logger("REQUEST: %s\n", read_buffer);

        std::string response;
        response = handle_request(read_buffer);

        /* +1 for null terminated character */
        if (sendto(fd, response.c_str(), (response.length() + 1), 0, 
                    (struct sockaddr*)&cli_addr, slen) == -1) {
            perror("sendto");
            continue;
        }

        logger("RESPONSE: %s\n", response.c_str());
    }

    pthread_exit(NULL);
}

void init_udp_server()
{
    pthread_create(&udp_client_thread, NULL, &udp_client_handler, NULL);
}
